#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <queue>
#include <time.h>
#include <pthread.h>
using namespace std;

template <class T>
class block_queue
{
public:
    block_queue(int cap)
        : _capacity(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_pro_cond, nullptr);
        pthread_cond_init(&_cons_cond, nullptr);
    }

    bool is_full()
    {
        return _capacity == _q.size();
    }
    bool is_empty()
    {
        return _q.size() == 0;
    }

    void push(const T &data) // 生产者
    {
        pthread_mutex_lock(&_mutex); // 防止多执行流干扰结果--上锁(生产者之间是互斥关系)

        while (is_full()) // 数据满了就要进行等待
        {
            pthread_cond_wait(&_pro_cond, &_mutex);
            // 当信号方式是broadcast的话，就会唤醒在该代码处等待所有线程，多线程会竞争信号，而失败的线程依旧在该锁处等待（等待成功会自动上锁），竞争成功的线程执行解锁完毕后，
            // 其他的线程就会被唤醒（伪唤醒）向下执行，如果此时没有数据的话就会出问题,所以将if换成while更合适,每个线程都要判断一遍，只有非空才能出循环
        }
        _q.push(data); // 生产后就向消费者发信号
        pthread_cond_signal(&_cons_cond);

        pthread_mutex_unlock(&_mutex);
    }

    void pop(T *data) // 消费者
    {
        pthread_mutex_lock(&_mutex); // 防止多执行流干扰结果--上锁(消费者之间是互斥关系)

        while (is_empty()) // 没数据了要等待
        {
            pthread_cond_wait(&_cons_cond, &_mutex); // 同理
        }
        *data = _q.front();
        _q.pop(); // 消费后就向生产者发信号
        pthread_cond_signal(&_pro_cond);

        pthread_mutex_unlock(&_mutex);
    }

    ~block_queue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pro_cond);
        pthread_cond_destroy(&_cons_cond);
    }

private:
    queue<T> _q;
    int _capacity;
    pthread_mutex_t _mutex;//生产者和消费者也是互斥同步关系（访问着同一个阻塞队列），维护一把锁
    pthread_cond_t _pro_cond;
    pthread_cond_t _cons_cond;
};